package com.weixin.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import com.weixin.pojo.message.Message;
import com.weixin.pojo.message.event.ScanReceiveEvent;
import com.weixin.pojo.message.reply.VideoReplyMessage;

/**
 * 类描述：xml工具类<br>
 * 创建人：邓强   <br>
 * 创建时间：2017年11月1日 下午6:05:47    <br> 
 * 修改人：  <br>
 * 修改时间：2017年11月1日 下午6:05:47   <br>  
 * 修改备注：     <br>
 * @version   V1.0
 */
@SuppressWarnings("unchecked")
public class WxXml {
	private static Logger log=LoggerFactory.getLogger(WxXml.class);
	
    /**
     * java 转换成xml
     * @Title: toXml 
     * @param obj 对象实例
     * @return String xml字符串
     */
    public static String toXml(Object obj){
        XStream xstream=new XStream(new WxXppDriver());
        
        ////如果没有这句，xml中的根元素会是<包.类名>；或者说：注解根本就没生效，所以的元素名就是类的属性
        xstream.processAnnotations(obj.getClass()); //通过注解方式的，一定要有这句话
        xstream.autodetectAnnotations(true);
        return xstream.toXML(obj);
    }
    
    /**
     *  将传入xml文本转换成Java对象
     * @Title: toBean 
     * @param xmlStr
     * @param cls  xml对应的class类
     * @return T   xml对应的class类的实例对象
     * 
     * 调用的方法实例：PersonBean person=XmlUtil.toBean(xmlStr, PersonBean.class);
     */
    public static <T> T  toBean(String xmlStr,Class<T> cls){
        //注意：不是new Xstream(); 否则报错：java.lang.NoClassDefFoundError: org/xmlpull/v1/XmlPullParserFactory
        XStream xstream=new ETLXstream();
        xstream.processAnnotations(cls);
        T obj=(T)xstream.fromXML(xmlStr);
        return obj;            
    } 
 

   /**
     * 写到xml文件中去
     * @Title: writeXMLFile 
     * @param obj 对象
     * @param absPath 绝对路径
     * @param fileName    文件名
     * @return boolean
     */
  
    public static boolean toXMLFile(Object obj, String absPath, String fileName ){
        String strXml = toXml(obj);
        String filePath = absPath + fileName;
        File file = new File(filePath);
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                log.error("创建{}文件失败!!!", filePath);
                return false ;
            }
        }// end if 
        OutputStream ous = null ;
        try {
            ous = new FileOutputStream(file);
            ous.write(strXml.getBytes());
            ous.flush();
        } catch (Exception e1) {
            log.error("写{}文件失败!!!", filePath);
            return false;
        }finally{
            if(ous != null )
                try {
                    ous.close();
                } catch (IOException e) {
                    log.error("写{}文件关闭输出流异常!!!",filePath);
                }
        }
        return true ;
    }
    
    /**
     * 从xml文件读取报文
     * @Title: toBeanFromFile 
     * @param absPath 绝对路径
     * @param fileName 文件名
     * @param cls
     * @throws Exception 
     * @return T
     */
    public static <T> T  toBeanFromFile(String absPath, String fileName,Class<T> cls) throws Exception{
        String filePath = absPath +fileName;
        InputStream ins = null ;
        try {
            ins = new FileInputStream(new File(filePath ));
        } catch (Exception e) {
            throw new Exception("读{"+ filePath +"}文件失败！", e);
        }
        
        XStream xstream=new XStream(new DomDriver());
        xstream.processAnnotations(cls);
        T obj =null;
        try {
            obj = (T)xstream.fromXML(ins);
        } catch (Exception e) {
            throw new Exception("解析{"+ filePath +"}文件失败！",e);
        }
        if(ins != null)
            ins.close();
        return obj;            
    } 
    
	/**
	 * 输入流转换成xml<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年11月6日 下午4:18:43    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年11月6日 下午4:18:43   <br>  
	 * 修改备注：     <br> 
	 * @param is
	 * @param enc
	 * @return
	 * @throws DocumentException
	 */
	public static String toXml(InputStream is,String... enc) throws DocumentException{
		SAXReader saxr=new SAXReader();
		if(enc!=null && enc.length>0 && enc[0]!=null){
			saxr.setEncoding(enc[0]);
		}
		Document document=saxr.read(is);
		
		Element root=document.getRootElement();
		return root.asXML();
	}
	
	
	public static void main(String[] args){
		String s="<xml><ToUserName><![CDATA[gh_05a3967e506f]]></ToUserName><FromUserName><![CDATA[opCrJt27SNyZqd_H95dbnFHa0RP0]]></FromUserName><CreateTime>1453190942</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[scancode_push]]></Event><EventKey><![CDATA[menu3]]></EventKey><ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType><ScanResult><![CDATA[http://weixin.qq.com/r/ckz27hPEh074rVeN9xlg]]></ScanResult></ScanCodeInfo></xml>";
		ScanReceiveEvent mr = toBean(s, ScanReceiveEvent.class);
		System.out.println(toXml(mr));
		Message message = new VideoReplyMessage("asdf", "sdfgae", "sfdgfjhsdhgr");
		String ss=toXml(message);
		System.out.println(ss);
	}
}

class ETLXstream extends XStream {
    /*
     * (non-Javadoc)
     * 
     * @see
     * com.thoughtworks.xstream.XStream#wrapMapper
     */
    @Override
    protected MapperWrapper wrapMapper(MapperWrapper next) {
        return new MapperWrapper(next) {
            /*
             * (non-Javadoc)
             * 
             * @see
             * com.thoughtworks.xstream.mapper.MapperWrapper#shouldSerializeMember
             */
            @Override
            public boolean shouldSerializeMember(@SuppressWarnings("rawtypes") Class definedIn, String fieldName) {
                // 不能识别的节点，掠过。
                if (definedIn == Object.class) {
                    return false;
                }
                // 节点名称为fileName的掠过
                if (fieldName.equals("fileName")) {
                    return false;
                }
                return super.shouldSerializeMember(definedIn, fieldName);
            }
        };
    }
}

class WxXppDriver extends XppDriver{
	WxXppDriver() {
		super(new XmlFriendlyNameCoder("_-", "_"));
	}
	
	@Override
	public HierarchicalStreamWriter createWriter(Writer out) {
		return new PrettyPrintWriter(out, super.getNameCoder()) {
			boolean cdata=true;
			
			@SuppressWarnings("rawtypes")
			@Override
			public void startNode(String alias, Class clzz) {
				super.startNode(alias, clzz);
				if(clzz==int.class || clzz==Integer.class || 
						clzz==long.class || clzz==Long.class || 
						clzz==float.class || clzz==Float.class || 
						clzz==double.class || clzz==Double.class){
					cdata=false;
				}else{
					cdata=true;
				}
			}

			@Override
			protected void writeText(QuickWriter writer, String text) {
				if(cdata){
					writer.write("<![CDATA[");  
                    writer.write(text);  
                    writer.write("]]>"); 
				}else{
					writer.write(text);
				}
			}
		};
    }
}
